package service

import (
	"context"
	"gitee.com/crack007/goose"
	"github.com/crack007/go-im/pb"
	"github.com/crack007/go-im/src/common/constant"
	"github.com/crack007/go-im/src/common/model"
	"google.golang.org/grpc"
	"strconv"
	"sync"
)

type nodeGrpcService struct {
	nodeClusterModel *model.NodeClusterModel
	lock             sync.Mutex
	CurrentNode      *model.NodeModel
}

func NewNodeGrpcService() *nodeGrpcService {
	return &nodeGrpcService{
		nodeClusterModel: model.NewNodeClusterModel(),
		CurrentNode:      model.NewNodeModel(),
	}
}

func (s *nodeGrpcService) OnPing(ctx context.Context, pingReq *pb.PingReq) (*pb.PongResp, error) {
	return &pb.PongResp{
		SourceId: s.CurrentNode.Id,
	}, nil
}
func (s *nodeGrpcService) AddNode(nodeModel *model.NodeModel) {
	goose.GetLogger().Debug("AddNode-> id:%s ip:%s port:%d wsPort:%d", nodeModel.Id, nodeModel.Ip, nodeModel.Port, nodeModel.WsPort)
	s.lock.Lock()
	s.nodeClusterModel.NodeMap[nodeModel.Id] = nodeModel
	s.lock.Unlock()
}
func (s *nodeGrpcService) AddSentinel(sentinelModel *model.SentinelModel) {
	goose.GetLogger().Debug("AddSentinel-> id:%s ip:%s port:%d", sentinelModel.Id, sentinelModel.Ip, sentinelModel.Port)
	s.lock.Lock()
	s.nodeClusterModel.SentinelMap[sentinelModel.Id] = sentinelModel
	s.lock.Unlock()
}
func (s *nodeGrpcService) OnConnectNode(ctx context.Context, nodeInfo *pb.NodeInfo) (*pb.NodeMapInfo, error) {
	goose.GetLogger().Debug("OnConnectNode-> id:%s ip:%s port:%d", nodeInfo.Id, nodeInfo.Ip, nodeInfo.Port)
	nodeModel := model.NewNodeModel()
	nodeModel.Id = nodeInfo.Id
	nodeModel.Port = uint16(nodeInfo.Port)
	nodeModel.WsPort = uint16(nodeInfo.WsPort)
	nodeModel.Ip = nodeInfo.Ip
	nodeModel.IsMaster = nodeInfo.IsMaster
	s.AddNode(nodeModel)
	// 广播给sentinel
	nodeBroadcast := &pb.NodeBroadcastReq{}
	nodeBroadcast.NodeMapInfo = s.GetNodeMapInfo()
	sourceNode := &pb.NodeInfo{}
	sourceNode.Id = s.CurrentNode.Id
	sourceNode.Port = uint32(s.CurrentNode.Port)
	sourceNode.Ip = s.CurrentNode.Ip
	sourceNode.IsMaster = s.CurrentNode.IsMaster
	nodeBroadcast.SourceNode = sourceNode
	for _, sentinelModel := range s.nodeClusterModel.SentinelMap {
		_, err := sentinelModel.Client.WorkerBroadcast(ctx, nodeBroadcast)
		if err != nil {
			return nil, err
		}
	}

	return s.GetNodeMapInfo(), nil
}

func (s *nodeGrpcService) OnConnectSentinel(ctx context.Context, sentinelInfo *pb.SentinelInfo) (*pb.NodeMapInfo, error) {
	goose.GetLogger().Debug("OnConnectSentinel->id:%s port:%d", sentinelInfo.Id, sentinelInfo.Port)
	sentinelModel := model.NewSentinelModel()
	sentinelModel.Ip = sentinelInfo.Ip
	sentinelModel.Id = sentinelInfo.Id
	sentinelModel.Port = uint16(sentinelInfo.Port)
	// 和Sentinel建立新的通道
	target := sentinelInfo.Ip + ":" + strconv.Itoa(int(sentinelInfo.Port))
	conn, err := grpc.Dial(target, grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		goose.GetLogger().Error(err)
		return nil, err
	}
	sentinelModel.Client = pb.NewSentinelServiceClient(conn)
	nodeBroadcast := &pb.NodeBroadcastReq{
		NodeMapInfo: &pb.NodeMapInfo{},
	}
	nodeBroadcast.NodeMapInfo.SentinelList = append(nodeBroadcast.NodeMapInfo.SentinelList, sentinelInfo)
	nodeBroadcast.SourceNode = model.GetNodeInfo(s.CurrentNode)
	// 广播到Sentinel
	for _, sentinel := range s.nodeClusterModel.SentinelMap {
		sentinel.Client.WorkerBroadcast(ctx, nodeBroadcast)
	}

	s.AddSentinel(sentinelModel)
	return s.GetNodeMapInfo(), nil
}

func (s *nodeGrpcService) SendUserMsg(ctx context.Context, userMsg *pb.UserMsg) (*pb.Resp, error) {
	goose.GetLogger().Debug("grpc receive msg->%s", userMsg.Data)
	messageModel := model.NewMessageModel()
	messageModel.MessageType = constant.MessageType(userMsg.MessageType)
	messageModel.ToUser = userMsg.ToUser
	messageModel.FromUser = userMsg.FromUser
	messageModel.FromNode = userMsg.FromNode
	messageModel.CreatedAt = userMsg.CreatedAt
	messageModel.Action = userMsg.Action
	messageModel.Data = userMsg.Data
	messageModel.MsgId = userMsg.MsgId
	MessageService.Send(messageModel)
	return &pb.Resp{}, nil
}
func (s *nodeGrpcService) GetNodeMapInfo() *pb.NodeMapInfo {
	nodeMapInfo := &pb.NodeMapInfo{}
	for _, nodeModel := range s.nodeClusterModel.NodeMap {
		nodeMapInfo.NodeList = append(nodeMapInfo.NodeList, model.GetNodeInfo(nodeModel))
	}
	for _, sentinelModel := range s.nodeClusterModel.SentinelMap {
		nodeMapInfo.SentinelList = append(nodeMapInfo.SentinelList, model.GetSentinelInfo(sentinelModel))
	}
	return nodeMapInfo
}
